package sketch.ast;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;

import sketch.ExecutableSequenceContent;
import sketch.SketchSequence;
import sketch.util.Checker;
import sketch.util.Globals;

public class MethodDeclarationWithHoles {
	
	public final MethodDeclaration declaration;
	
	public final Map<MethodInvocation, GeneratorHole> maps;
	
	public final Map<MethodInvocation, Expression> receivers;
	
	public final Set<Statement> removes;
	
	public MethodDeclarationWithHoles(MethodDeclaration declaration, Map<MethodInvocation, GeneratorHole> maps
			, Map<MethodInvocation, Expression> receivers, Set<Statement> removedStatements) {
		Checker.checkNull(declaration, "The method declaration could not be null.");
		Checker.checkNull(maps, "The method invocation map could not be null.");
		Checker.checkNull(receivers, "The method invocation receivers map could not be null.");
		Checker.checkTrue(maps.size() == receivers.size(), "The size of two maps should be equal!");
		Checker.checkTrue(maps.keySet().equals(receivers.keySet()), "The key set should be exactly the same!");
		
		this.declaration = declaration;
		this.maps = maps;
		this.receivers = receivers;
		this.removes = removedStatements;
	}
	
	//XXX unfinished
	public void fillinHoles(List<ExecutableSequenceContent[]> contents, Class<?>[] generatorTypes) {
		Checker.checkTrue(maps.size() == contents.size(), "The number of holes : " + maps.size() + " should be " +
				"the same number as executable sequence content: " + contents.size());
		Checker.checkTrue(maps.size() == generatorTypes.length,  "The number of holes should be " +
		"the same number as the generator types!");
		
		int index = 0;
		for(MethodInvocation methodInvoke : maps.keySet()) {
			//the string builder for dumping
			StringBuilder sb = new StringBuilder();
			GeneratorHole hole = this.maps.get(methodInvoke);
			Expression receiver = this.receivers.get(methodInvoke);
			
			Checker.checkNull(hole, "The generator hole should not be null!");
			Checker.checkNull(receiver, "The receiver expression should not be null!");
			
			ExecutableSequenceContent[] contentArray = contents.get(index);
			Class<?> generatorType = generatorTypes[index];
			boolean assertResult = generatorType.equals(sketch.generator.AssertionGenerator.class);
			
			int count = 0;
			for(ExecutableSequenceContent singleContent : contentArray) {
				String code = singleContent.toCodeString(assertResult);
				String receiverString = receiver.toString();
				if(!(receiver instanceof Name)) {
					receiverString = "(" + receiverString + ")";
				}
				code = code.replace(SketchSequence.RECEIVER, receiverString);
				sb.append(code);
				//sb.append(";");
				if(count != contentArray.length -1) {
					sb.append(";");
				}
				sb.append(Globals.lineSep);
				count++;
			}
			
			//set the hole cotent
			hole.setContent(sb.toString());
			index++;
		}
		
	}
	
	public void rename(String name) {
		SimpleName simpleName = this.declaration.getName();
		simpleName.setIdentifier(name);
	}
	
	public void removeAnnotation() {
		//this.declaration.
		List modifiers = this.declaration.modifiers();
		int index = 0;
		for(; index < modifiers.size(); index++) {
			if(modifiers.get(index).toString().indexOf("@") != -1) {
				break;
			}
		}
		modifiers.remove(index);
	}
	
	@Override
	public String toString() {
		GeneratorCodePrinter printer = new GeneratorCodePrinter(this);
		printer.visit(this.declaration);
		
		return printer.getResult();
	}
}